package population.phaseExecutors;

import java.util.ArrayList;
import java.util.Random;

import character.Character;
import character.Gender;
import character.SocialLinks;
import character.race.Race;
import character.race.RaceProperties;
import population.Population;
import population.populationFindPredicates.FindAgeRange;
import population.populationFindPredicates.FindAllButSpecificCharacter;
import population.populationFindPredicates.FindGender;
import population.populationFindPredicates.FindRace;
import population.populationFindPredicates.FindLookingForLove;
import population.populationFindPredicates.FindRandom;
import population.populationFindPredicates.FindSocialLink;
import population.populationFindPredicates.PopulationFindPredicate;
import utilities.TimeProperties;

/**
 * Find love phase.
 * @author pcdejager
 */
public class FindLovePhase implements PhaseExecutor {
    /**
     * Constructor.
     * @param r The race to find love for
     */
    public FindLovePhase(final String r) {
        race = r;
        probability = RaceProperties.getInstance().get(r)
                .getRates().getLoveRate();
    }

    /**
     * Execute phase.
     * @param p Population to execute on
     */
    public final void execute(final Population p) {
        Random random = new Random();
        // Scale with days in year
        double prob = probability
                / ((double) TimeProperties.getInstance().daysInAYear());
        // TODO : Determine probability from race
        ArrayList<Character> available = findInitial(p, prob);
        if (available.isEmpty()) {
            return;
        }
        for (Character p1 : available) {
            // If already linked
            if (p1.getSocialLinks().getLoveInterest() != null) {
                continue;
            }
            // Check start age
            Race r = RaceProperties.getInstance().get(race);
            if (r.getPartner().getPartnerStartAge() > p1.getAge().years()) {
                continue;
            }
            // Find possibles
            ArrayList<Character> possible = findPossible(p, p1);
            if (possible.isEmpty()) {
                continue;
            }
            // Choose one of possibles
            int pos2 = random.nextInt(possible.size());
            Character p2 = possible.get(pos2);
            if (p1 == p2) {
                continue;
            }
            // Link
            boolean ok = SocialLinks.fallInLove(p1, p2);
            if (!ok) {
                // TODO : What now?
                continue;
            }
        }
    }

    /**
     * Find characters without love interests.
     * @param p Population to search
     * @param prob The probability
     * @return The list of available individuals
     */
    private ArrayList<Character> findInitial(final Population p,
            final double prob) {
        // TODO : this result could be randomized to enforce more randomness
        return p.find(
                new PopulationFindPredicate[] {
                        new FindLookingForLove(),
                        new FindRace(race),
                        new FindRandom(prob)});
    }

    /**
     * Find possible love interests.
     * @param p The population to search
     * @param c The character seeking a love interest
     * @return The list of available individuals
     */
    private ArrayList<Character> findPossible(
            final Population p,
            final Character c) {
        Race r = c.getRace();
        int minAge = r.getPartner().getPartnerMinimumAge(c.getAge().years());
        int maxAge = r.getPartner().getPartnerMaximumAge(c.getAge().years());

        // TODO : For now same sex love interest not catered for
        Gender gender = Gender.FEMALE;
        if (c.getGender() == Gender.FEMALE) {
            gender = Gender.MALE;
        }
        return p.find(
                new PopulationFindPredicate[] {
                        new FindRace(race),
                        new FindAgeRange(minAge, maxAge),
                        new FindGender(gender),
                        new FindSocialLink(
                                FindSocialLink.Link.LOVEINTEREST,
                                FindSocialLink.Condition.DOESNOTEXIST),
                        new FindAllButSpecificCharacter(c)});
    }

    /**
     * The race to find love for.
     */
    private final String race;
    /**
     * The probability.
     */
    private final double probability;
}
